#include "InputData.h"

InputData::InputData(string file_path){
	UINTN ext_file_size;
	struct stat file_stat;
	InputFile.open(file_path.c_str(), ios::in | ios::binary);
	UINTN file_size;
	
	this->file_path=file_path;
	if(!InputFile){
		cerr<<"Unable to open file: "<<file_path<<endl;
		exit(2);
	}
	
	stat(file_path.c_str(), &file_stat);
	file_size = file_stat.st_size;
	if(file_size<10){
		cerr<<"Invalid file format (too short)"<<endl;
		exit(2);
	}
	InputFile.read((char *)&ntrack, sizeof(ntrack));
	InputFile.read((char *)&nsample, sizeof(nsample));
	InputFile.read((char *)&sample_format, sizeof(sample_format));
	InputFile.read((char *)&text_size, sizeof(text_size));
	
	track_size=FormatToSize(sample_format)*nsample;
	ext_file_size=10+(track_size+text_size)*ntrack;
	if(file_size!=ext_file_size){
		cerr<<"Invalid file size: "<<file_size<<" (it should be "<<
					ext_file_size<<")"<<endl;
		exit(2);
	}
	
	cout<<file_path<<": "<<ntrack <<" tracks, " <<nsample<<" samples per track, "
				<<FormatToSize(sample_format)<<" bytes per sample"<<endl;
}
InputData::~InputData(){
	InputFile.close();
}
Matrix* InputData::ReadTexts(ConsumptionModel* cm,short mem){
	return ReadTexts(1,ntrack,cm,mem);
}
Matrix* InputData::ReadTexts(UINTN first,UINTN last,ConsumptionModel* cm,short mem){
	UINTN i,j;
	uint8_t k,z;
	int8_t b;
	Matrix* m;
	UINTN ntexts=last-first+1;
	uint8_t* text = (uint8_t*) malloc(text_size);
	REALN* data;
	REALN* cuda_data;
	
	data = (REALN*) malloc(256*ntexts*sizeof(REALN));
	
	if(last<first){
		m = new Matrix();
		return m;
	}
	
	cout<<file_path<<": reading texts from "<<first<<" to "<<last<<endl;
	InputFile.seekg (10, ios::beg);
	InputFile.seekg((track_size+text_size)*(first-1), ios::cur);
	for(i=first;i<=last;i++){
		InputFile.seekg(track_size, ios::cur);
		for(j=0;j<text_size;j++){
			InputFile.read((char*)&b,1);
			text[j]=(uint8_t)b;
			//cout<<text[j]<<endl;
			//cout<<hex<<(int)text[j]<<endl;
		}
		k=0;
		do{
			for(z=0;z<text_size;z++){
				text[z] ^= k;
			}
			data[k*ntexts+(i-first)]=cm->getConsumption(text,text_size);
			k++;
		}while(k!=0);
	}
	if(mem==CPU_MEM){
		m = new Matrix(256,ntexts,data,CPU_MEM);
	}else{
		CUDA_SC(cudaMalloc((void**)&cuda_data,256*ntexts*sizeof(REALN)));
		CUDA_SC(cudaMemcpy(cuda_data,data,256*ntexts*sizeof(REALN),cudaMemcpyHostToDevice));
		m = new Matrix(256,ntexts,cuda_data,DEVICE_MEM);
		free(data);
	}
	free(text);
	return m;
}
Matrix* InputData::ReadTracks(short mem,UINTN start,UINTN end){
	UINTN i,j;
	int8_t b;
	float f;
	double d;
	Matrix* m;
	uint8_t* text = (uint8_t*) malloc(text_size);
	REALN* data;
	REALN* cuda_data;
	
	data = (REALN*) malloc((end-start)*sizeof(REALN)*ntrack);
	//cout<<start<<" -> "<<end<<endl;
	//cout<<endl;
	//cout<<file_path<<": reading tracks from "<<first<<" to "<<last<<endl;
	for(i=0;i<ntrack;i++){
		InputFile.seekg(10+(track_size+text_size)*i, ios::beg);
		InputFile.seekg(FormatToSize(sample_format)*start,ios::cur);
		//cout<<InputFile.tellg();
		for(j=0;j<(end-start);j++){
			if(sample_format == 'b'){
				InputFile.read((char*)&b,FormatToSize(sample_format));
				data[j*ntrack+i]=(REALN)b;
			}else if(sample_format == 'f'){
				InputFile.read((char*)&f,FormatToSize(sample_format));
				data[j*ntrack+i]=(REALN)f;
			}else if(sample_format == 'd'){
				InputFile.read((char*)&d,FormatToSize(sample_format));
				data[j*ntrack+i]=(REALN)d;
			}
		}
		//cout<<" -> "<<InputFile.tellg()<<endl;
	}
	if(mem==CPU_MEM){
		m = new Matrix((end-start),ntrack,data,CPU_MEM);
	}else{
		CUDA_SC(cudaMalloc((void**)&cuda_data,(end-start)*sizeof(REALN)*ntrack));
		CUDA_SC(cudaMemcpy(cuda_data,data,(end-start)*sizeof(REALN)*ntrack,cudaMemcpyHostToDevice));
		m = new Matrix((end-start),ntrack,cuda_data,DEVICE_MEM);
		free(data);
	}
	return m;
}
UINTN InputData::FormatToSize(char format){
	switch(format){
		case 'b':
			return 1;
		case 'f':
			return 4;
		case 'd':
			return 8;
	}
	cerr<<"Invalid sample format: "<<format<<endl;
	exit(2);
}



